package com.lvhr.rpc.utils;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;

import cn.hutool.core.io.FileUtil;
import com.lvhr.rpc.constant.RpcConstant;
import org.apache.commons.collections.CollectionUtils;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;


import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;


/**
 * 配置工具类
 */
public class ConfigUtils {

    private static final AtomicReference<Map<String, Object>> CONFIG_CACHE = new AtomicReference<>();
    private static ConfigWatcher configWatcher; // 自定义监听器
    private static final String[] CONFIG_EXTENSIONS = {".yml", ".yaml", ".properties"};
    private static final Charset CONFIG_CHARSET = StandardCharsets.UTF_8;
    private static final String BASE_NAME = "application";

    /**
     * 加载配置对象(默认环境)
     *
     * @param tClass 配置类类型
     * @param prefix 配置前缀
     * @param <T>    泛型类型
     * @return 配置对象
     */
    public static <T> T loadConfig(Class<T> tClass, String prefix) {
        return loadConfig(tClass, prefix, "", RpcConstant.DEFAULT_CONFIG_SYS_ORDER);
    }

    // Hutool 的 Props 类加载配置文件（支持编码和前缀）
//    public static <T> T loadConfig(Class<T> tClass, String prefix, String environment) {
//        StringBuilder configFileBuilder = new StringBuilder("application");
//        if (StrUtil.isNotBlank(environment)) {
//            configFileBuilder.append("-").append(environment);
//        }
//        configFileBuilder.append(".properties");
//        Props props = new Props(configFileBuilder.toString(),StandardCharsets.UTF_8);
//        return props.toBean(tClass, prefix);
//        String fileName = "application" + (StrUtil.isNotBlank(environment) ? "-" + environment : "") + ".properties";
//        Props props = new Props(fileName, StandardCharsets.UTF_8);
//        // 调试输出：打印所有键值对，检查中文是否正常
//        props.forEach((key, value) -> System.out.println(key + " = " + value));
//        return props.toBean(tClass, prefix); // 直接支持前缀
//    }

    /**
     * 加载配置对象（支持多环境）
     * 加载优先级：yml > yaml > properties > 环境变量 > 系统属性
     *
     * @param tClass        配置类
     * @param prefix        配置前缀
     * @param environment   配置env
     * @param priorityOrder 配置文件后缀
     * @param <T>           泛型
     * @return
     */
    public static <T> T loadConfig(Class<T> tClass, String prefix, String environment, List<String> priorityOrder) {
        //  初始化容器
        Map<String, Object> configMap = new LinkedHashMap<>();

        // 1. 获取配置文件列表
        List<String> configFiles = resolveConfigFiles(environment, priorityOrder);

        // 2. 加载配置
        configMap = loadAndMergeConfigs(configFiles);
        // 2. 合并系统属性和环境变量（系统属性优先级最高）
        mergeSystemProperties(configMap);
//        configMap.putAll(System.getenv());
        // 3. 初始化监听器和缓存
        initConfigWatcher(configFiles, configMap);

        // 4. 映射到配置对象
        return mapToBean(configMap, tClass, prefix);
    }


    /**
     * 获取文件配置
     * @param environment
     * @param priorityOrder
     * @return
     */
    private static List<String> resolveConfigFiles(String environment, List<String> priorityOrder) {
        List<String> configFiles = new ArrayList<>();
        if (CollectionUtils.isEmpty(priorityOrder)) {
            configFiles = getConfigFiles(environment); //获取文件信息
        } else if (CollectionUtils.isNotEmpty(priorityOrder)){
            configFiles = new ArrayList<>(priorityOrder); // 直接使用参数
            configFiles.addAll(getConfigFiles(environment, priorityOrder));
        }
        return configFiles;
    }
    /**
     * 确保后加载的配置覆盖前配置
     * @param files
     * @return
     */
    private static Map<String, Object> loadAndMergeConfigs(List<String> files) {
        Map<String, Object> configMap = new LinkedHashMap<>();
        for (String listFile : files) {
            if (FileUtil.exist(listFile)) {
                try {
                    Map<String, Object> loaded = loadFile(listFile);
                    configMap.putAll(loaded);
                } catch (Exception e) {
                    throw new RuntimeException("加载配置文件失败: " + listFile, e);
                }
            }
        }
        return configMap;
    }
    /**
     * 初始化监听器和缓存
     * @param configFiles
     * @param newConfigMap
     */
    private static void initConfigWatcher(List<String> configFiles, Map<String, Object> newConfigMap) {
        if (CONFIG_CACHE.get() == null) {
            CONFIG_CACHE.set(Collections.unmodifiableMap(newConfigMap));
            //  初始化自定义监听器
            configWatcher = new ConfigWatcher(configFiles, updatedConfig -> {
                // 合并系统属性（保持最高优先级）
                mergeSystemProperties(updatedConfig);
                // 原子更新配置缓存
                CONFIG_CACHE.set(Collections.unmodifiableMap(updatedConfig));
                System.out.println("[HotUpdate] 配置已刷新: " + updatedConfig);
            });
            configWatcher.start();
        } else {
            CONFIG_CACHE.set(Collections.unmodifiableMap(newConfigMap));
        }
    }

    /**
     * 合并系统属性和环境变量
     * @param configMap
     */
    private static void mergeSystemProperties(Map<String, Object> configMap) {
        // 系统属性可覆盖文件配置
        System.getProperties().forEach((k, v) ->
                configMap.put(k.toString(), v.toString())
        );
    }
    /**
     * 将Map映射到JavaBean
     * @param configMap 配置文件map
     * @param tClass 配置类
     * @param prefix 配置前缀
     * @param <T> 泛型
     * @return
     */
    private static <T> T mapToBean(Map<String, Object> configMap, Class<T> tClass, String prefix) {
        Assert.notNull(tClass, "配置类不能为空");
        try {
            T config = tClass.getDeclaredConstructor().newInstance();

            // 流操作
            Map<String, Object> filteredMap = configMap.entrySet()
                    .stream()
                    .filter(e -> e.getKey().startsWith(prefix + "."))
                    .collect(Collectors.toMap(
                            e -> e.getKey().substring(prefix.length() + 1),
                            Map.Entry::getValue
                    ));

            org.apache.commons.beanutils.BeanUtils.populate(config, filteredMap);
            return config;
        } catch (Exception e) {
            throw new RuntimeException("配置映射到对象失败", e);
        }
    }


    /**
     * 获取配置文件列表（支持自定义后缀优先级）
     * @param environment 环境标识（如 dev/prod）
     * 优先级：yml > yaml > properties
     */
    private static List<String> getConfigFiles(String environment) {
        List<String> files = new ArrayList<>();
        //判断环境标识是否为空，拼接数据
        String envSuffix = StrUtil.isBlank(environment) ? "" : "-" + environment;

        //是否为空 ?  skip ：add
        if (StrUtil.isNotBlank(environment)){
            // 优先级：yml > yaml > properties
            files.add("application" + envSuffix + ".properties");
            files.add("application" + envSuffix + ".yaml");
            files.add("application" + envSuffix + ".yml");
        }

        // 加载默认配置（无环境后缀）
        if (StrUtil.isBlank(environment)) {
            files.add("application.properties");
            files.add("application.yaml");
            files.add("application.yml");
        }
        return files;
    }

    /**
     * 获取配置文件列表（支持自定义后缀优先级）
     * @param environment 环境标识（如 dev/prod）
     * @param priorityOrder 自定义后缀顺序（如 ["properties", "yaml", "yml"]）
     */
    private static List<String> getConfigFiles(String environment, List<String> priorityOrder) {
        List<String> files = new ArrayList<>();

        // 参数校验：priorityOrder 不能为 null 或空
        if (priorityOrder == null || priorityOrder.isEmpty()) {
            // TODO 可抛出异常或提供默认值（示例中返回空列表并记录日志）
            return files;
        }

        String envSuffix = StrUtil.isBlank(environment) ? "" : "-" + environment;

        // 1. 加载默认配置（application.xxx）
        for (String suffix : priorityOrder) {
            files.add("application." + suffix);
        }

        // 2. 加载环境配置
        if (StrUtil.isNotBlank(environment)) {
            for (String suffix : priorityOrder) {
                files.add("application" + envSuffix + "." + suffix);
            }
        }

        return files;
    }



    /**
     * 加载单个配置文件
     */
    private static Map<String, Object> loadFile(String filePath) {
        if (filePath.endsWith(".properties")) {
            return loadProperties(filePath);
        } else if (filePath.endsWith(".yml") || filePath.endsWith(".yaml")) {
            return loadYaml(filePath);
        }
        throw new UnsupportedOperationException("不支持的配置文件类型: " + filePath);
    }

    /**
     * 加载Properties文件（UTF-8编码）
     */
    private static Map<String, Object> loadProperties(String filePath) {
        Properties props = new Properties();
        try (InputStreamReader reader = new InputStreamReader(
                Objects.requireNonNull(ConfigUtils.class.getClassLoader().getResourceAsStream(filePath)),
                StandardCharsets.UTF_8)) {
            props.load(reader);
            // 新增：统一去除值中的引号
            props.replaceAll((k, v) ->
                    v.toString().replaceAll("^\"|\"$", "")); // 去除首尾引号
        } catch (Exception e) {
            throw new RuntimeException("读取Properties文件失败: " + filePath, e);
        }
        return new HashMap(props);
    }

    /**
     * 加载YAML文件并扁平化处理
     * @param filePath 配置文件路径
     * @return
     */
    private static Map<String, Object> loadYaml(String filePath) {
        Yaml yaml = new Yaml();
        try (InputStreamReader reader = new InputStreamReader(
                Objects.requireNonNull(ConfigUtils.class.getClassLoader().getResourceAsStream(filePath)),
                StandardCharsets.UTF_8)) {
            Map<String, Object> yamlMap = yaml.load(reader);
            // 空 Map 处理
            if (yamlMap == null || yamlMap.isEmpty()) {
                return Collections.emptyMap();
            }
            return flattenMap(yamlMap);
        } catch (Exception e) {
            throw new RuntimeException("读取YAML文件失败: " + filePath, e);
        }
    }

    /**
     * 将嵌套Map转换为扁平化结构（key格式：parent.child）
     * @param source 配置文件map
     * @return
     */
    private static Map<String, Object> flattenMap(Map<String, Object> source) {
        Map<String, Object> result = new HashMap<>();
        flatten("", source, result);
        return result;
    }

    /**
     *
     * @param prefix 配置前缀
     * @param source 目标map
     * @param result 返回map
     */
    private static void flatten(String prefix, Map<String, Object> source, Map<String, Object> result) {
        //遍历原始map
        source.forEach((key, value) -> {
            //构建完整路径
            String fullKey = prefix.isEmpty() ? key : prefix + "." + key;
            //处理嵌套Map
            if (value instanceof Map) {
                //递归展开嵌套（深度优先遍历）
                flatten(fullKey, (Map<String, Object>) value, result);
            } else if (value != null){
                //存储叶子节点（键值对）
//                result.put(fullKey, value.toString());
                //保留原始类型
                result.put(fullKey, value.toString());
            }
        });
    }

/*    private static String buildFileName(String environment, String extension) {
        StringBuilder sb = new StringBuilder("application");
        if (StrUtil.isNotBlank(environment)) {
            sb.append("-").append(environment);
        }
        return sb.append(".").append(extension).toString();
    }*/
/*    private static <T> T parseConfig(ConfigFile configFile, Class<T> tClass, String prefix) {
        try (InputStream input = new FileInputStream(configFile.fileName)) {
            Map<String, Object> configMap;
            if (configFile.isYaml()) {
                Yaml yaml = new Yaml(new SafeConstructor());
                configMap = yaml.load(input);
            } else {
                Properties props = new Properties();
                props.load(new InputStreamReader(input, CONFIG_CHARSET));
                configMap = new HashMap<>(props);
            }

            // 支持嵌套配置转换
            return convertNestedConfig(configMap, tClass, prefix);
        } catch (Exception e) {
            throw new ConfigException("Config load failed", e);
        }
    }*/

}
